﻿using System;
using System.Collections;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Threading;

namespace ICodeShare.UI.Controls
{
    [TemplatePart(Name = PART_Editor, Type = typeof(TextBox))]
    [TemplatePart(Name = PART_Popup, Type = typeof(Popup))]
    [TemplatePart(Name = PART_Selector, Type = typeof(Selector))]
    public class AutoCompleteTextBox : Control
    {
        #region Constants

        public const string PART_Editor = "PART_Editor";
        public const string PART_Popup = "PART_Popup";
        public const string PART_Selector = "PART_Selector";

        #endregion Constants

        #region Members
        private bool _isUpdatingText;
        private bool _selectionCancelled;
        private SuggestionsAdapter _suggestionsAdapter;
        private TextBox _editor;
        private Popup _popup;
        private Selector _itemsSelector;
        private BindingEvaluator<string> _valueBindingEvaluator;
        private DispatcherTimer _fetchTimer;
        private SelectionAdapter _selectionAdapter;
        #endregion Members

        #region Constructors

        static AutoCompleteTextBox()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(typeof(AutoCompleteTextBox)));
        }

        #endregion Constructors

        #region Properties

        #region Delay 绑定触发延迟

        /// <summary>
        /// 绑定触发延迟
        /// </summary>
        public int Delay
        {
            get { return (int)GetValue(DelayProperty); }

            set { SetValue(DelayProperty, value); }
        }

        public static readonly DependencyProperty DelayProperty = DependencyProperty.Register(
            "Delay", 
            typeof(int), 
            typeof(AutoCompleteTextBox), 
            new FrameworkPropertyMetadata(200));

        #endregion Delay 绑定触发延迟

        #region DisplayMember 展示的列

        /// <summary>
        /// 展示的列
        /// </summary>
        public string DisplayMember
        {
            get { return (string)GetValue(DisplayMemberProperty); }

            set { SetValue(DisplayMemberProperty, value); }
        }

        public static readonly DependencyProperty DisplayMemberProperty = DependencyProperty.Register(
            "DisplayMember", 
            typeof(string), 
            typeof(AutoCompleteTextBox), 
            new FrameworkPropertyMetadata(string.Empty));


        #endregion

        #region IsDropDownOpen 下拉框是否展开

        /// <summary>
        /// 下拉框是否展开
        /// </summary>
        public bool IsDropDownOpen
        {
            get { return (bool)GetValue(IsDropDownOpenProperty); }

            set { SetValue(IsDropDownOpenProperty, value); }
        }

        public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register(
            "IsDropDownOpen", 
            typeof(bool), 
            typeof(AutoCompleteTextBox), 
            new FrameworkPropertyMetadata(false));

        #endregion

        #region IsDropDownOpenOnFocus 获取焦点时，下拉框是否展开

        /// <summary>
        /// 获取焦点时，下拉框是否展开
        /// </summary>
        public bool IsDropDownOpenOnFocus
        {
            get { return (bool)GetValue(IsDropDownOpenOnFocusProperty); }

            set { SetValue(IsDropDownOpenOnFocusProperty, value); }
        }

        public static readonly DependencyProperty IsDropDownOpenOnFocusProperty = DependencyProperty.Register(
            "IsDropDownOpenOnFocus", 
            typeof(bool), 
            typeof(AutoCompleteTextBox),
            new FrameworkPropertyMetadata(false, OnIsDropDownOpenOnFocusChanged));

           /// <summary>
        /// 选中项改变事件
        /// </summary>
        public static void OnIsDropDownOpenOnFocusChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
        {
            AutoCompleteTextBox txtAutoComplete = dependencyObject as AutoCompleteTextBox;
             if (txtAutoComplete != null)
            {
                if (txtAutoComplete._popup != null )
                {
                    txtAutoComplete._popup.StaysOpen =(bool)args.NewValue;
                }
            }
        }

        #endregion

        #region IsLoading 是否正在加载结果

        /// <summary>
        /// 是否正在加载结果
        /// </summary>
        public bool IsLoading
        {
            get { return (bool)GetValue(IsLoadingProperty); }

            set { SetValue(IsLoadingProperty, value); }
        }

        public static readonly DependencyProperty IsLoadingProperty = DependencyProperty.Register(
            "IsLoading", 
            typeof(bool), 
            typeof(AutoCompleteTextBox), 
            new FrameworkPropertyMetadata(false));

        #endregion

        #region IsReadOnly 是否只读

        /// <summary>
        /// 是否只读
        /// </summary>
        public bool IsReadOnly
        {
            get { return (bool)GetValue(IsReadOnlyProperty); }

            set { SetValue(IsReadOnlyProperty, value); }
        }

        public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register(
            "IsReadOnly", 
            typeof(bool), 
            typeof(AutoCompleteTextBox), 
            new FrameworkPropertyMetadata(false));

        #endregion

        #region ItemTemplate 子项数据模板

        /// <summary>
        /// 子项数据模板
        /// </summary>
        public DataTemplate ItemTemplate
        {
            get { return (DataTemplate)GetValue(ItemTemplateProperty); }

            set { SetValue(ItemTemplateProperty, value); }
        }

        public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register(
            "ItemTemplate", 
            typeof(DataTemplate), 
            typeof(AutoCompleteTextBox), 
            new FrameworkPropertyMetadata(null));

        #endregion

        #region ItemTemplateSelector 子项数据数据模板选择器

        /// <summary>
        /// 子项数据数据模板选择器
        /// </summary>
        public DataTemplateSelector ItemTemplateSelector
        {
            get { return ((DataTemplateSelector)(GetValue(AutoCompleteTextBox.ItemTemplateSelectorProperty))); }
            set { SetValue(AutoCompleteTextBox.ItemTemplateSelectorProperty, value); }
        }

        public static readonly DependencyProperty ItemTemplateSelectorProperty = DependencyProperty.Register(
            "ItemTemplateSelector", 
            typeof(DataTemplateSelector),
            typeof(AutoCompleteTextBox));

        #endregion

        #region LoadingContent 正在加载效果

        /// <summary>
        /// 正在加载效果
        /// </summary>
        public object LoadingContent
        {
            get { return GetValue(LoadingContentProperty); }

            set { SetValue(LoadingContentProperty, value); }
        }

        public static readonly DependencyProperty LoadingContentProperty = DependencyProperty.Register(
            "LoadingContent",
            typeof(object), 
            typeof(AutoCompleteTextBox), 
            new FrameworkPropertyMetadata(null));

        #endregion LoadingContent 正在加载效果

        #region Provider 数据筛选器

        /// <summary>
        /// 数据筛选器
        /// </summary>
        public ISuggestionProvider Provider
        {
            get { return (ISuggestionProvider)GetValue(ProviderProperty); }

            set { SetValue(ProviderProperty, value); }
        }

        public static readonly DependencyProperty ProviderProperty = DependencyProperty.Register(
            "Provider",
            typeof(ISuggestionProvider), 
            typeof(AutoCompleteTextBox), 
            new FrameworkPropertyMetadata(null));

        #endregion Provider 数据筛选器

        #region SelectedItem 选中项

        /// <summary>
        /// 选中项
        /// </summary>
        public object SelectedItem
        {
            get { return GetValue(SelectedItemProperty); }

            set { SetValue(SelectedItemProperty, value); }
        }

        public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register(
            "SelectedItem", 
            typeof(object),
            typeof(AutoCompleteTextBox), 
            new FrameworkPropertyMetadata(null, OnSelectedItemChanged));

      

        /// <summary>
        /// 选中项改变事件
        /// </summary>
        public static void OnSelectedItemChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
        {
            AutoCompleteTextBox txtAutoComplete = dependencyObject as AutoCompleteTextBox;
             if (txtAutoComplete != null)
            {
                if (txtAutoComplete._editor != null & !txtAutoComplete._isUpdatingText)
                {
                    txtAutoComplete._isUpdatingText = true;
                    txtAutoComplete._editor.Text = txtAutoComplete._valueBindingEvaluator.GetDynamicValue(args.NewValue);
                    txtAutoComplete._isUpdatingText = false;
                }
            }
        }

        #endregion SelectedItem

        #region Text 文本内容

        /// <summary>
        /// 文本内容
        /// </summary>
        public string Text
        {
            get { return (string)GetValue(TextProperty); }

            set { SetValue(TextProperty, value); }
        }

        public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
            "Text",
            typeof(string), 
            typeof(AutoCompleteTextBox), 
            new FrameworkPropertyMetadata(string.Empty));

        #endregion Text 文本内容

        #region MaxLength 最大长度

        /// <summary>
        /// 最大长度
        /// </summary>
        public int MaxLength
        {
            get { return (int)GetValue(MaxLengthProperty); }
            set { SetValue(MaxLengthProperty, value); }
        }

        public static readonly DependencyProperty MaxLengthProperty = DependencyProperty.Register(
            "MaxLength", 
            typeof(int), 
            typeof(AutoCompleteTextBox), 
            new FrameworkPropertyMetadata(0));

        #endregion
        

        #region CharacterCasing 字符大小写

        /// <summary>
        /// 字符大小写
        /// </summary>
        public CharacterCasing CharacterCasing
        {
            get { return (CharacterCasing)GetValue(CharacterCasingProperty); }
            set { SetValue(CharacterCasingProperty, value); }
        }

        public static readonly DependencyProperty CharacterCasingProperty = DependencyProperty.Register(
            "CharacterCasing", 
            typeof(CharacterCasing),
            typeof(AutoCompleteTextBox), 
            new FrameworkPropertyMetadata(CharacterCasing.Normal));

        #endregion CharacterCasing 大小写

        #region MaxDropDownHeight 弹窗最大高度

        /// <summary>
        /// 弹窗最大高度
        /// </summary>
        public double MaxDropDownHeight
        {
            get { return (double)GetValue(MaxDropDownHeightProperty); }
            set { SetValue(MaxDropDownHeightProperty, value); }
        }

        public static readonly DependencyProperty MaxDropDownHeightProperty = DependencyProperty.Register(
            "MaxDropDownHeight",
            typeof(double),
            typeof(AutoCompleteTextBox),
            new FrameworkPropertyMetadata(360.0));

        #endregion MaxPopupHeight

        #region Filter 筛选字符串

        private string _filter;
        /// <summary>
        /// 筛选字符串
        /// </summary>
        public string Filter
        {
            get { return _filter; }
            set { _filter = value; }
        }

        #endregion

        #endregion Properties

        #region "Methods"

        private void ScrollToSelectedItem()
        {
            ListBox listBox = _itemsSelector as ListBox;
            if (listBox != null && listBox.SelectedItem != null)
                listBox.ScrollIntoView(listBox.SelectedItem);
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            _editor = Template.FindName(PART_Editor, this) as TextBox;
            _popup = Template.FindName(PART_Popup, this) as Popup;
            _itemsSelector = Template.FindName(PART_Selector, this) as Selector;
            _valueBindingEvaluator = new BindingEvaluator<string>(new Binding(DisplayMember));

            if (_editor != null)
            {
                _editor.TextChanged += OnEditorTextChanged;
                _editor.PreviewKeyDown += OnEditorKeyDown;
                _editor.LostFocus += OnEditorLostFocus;

                if (SelectedItem != null)
                {
                    _editor.Text = _valueBindingEvaluator.GetDynamicValue(SelectedItem);
                }
            }

            this.GotFocus += AutoCompleteTextBox_GotFocus;

            if (_popup != null)
            {
                _popup.StaysOpen = false;
                _popup.Opened += OnPopupOpened;
                _popup.Closed += OnPopupClosed;
            }
            if (_itemsSelector != null)
            {
                _selectionAdapter = new SelectionAdapter(_itemsSelector);
                _selectionAdapter.Commit += OnSelectionAdapterCommit;
                _selectionAdapter.Cancel += OnSelectionAdapterCancel;
                _selectionAdapter.SelectionChanged += OnSelectionAdapterSelectionChanged;
            }
        }

        private void AutoCompleteTextBox_GotFocus(object sender, RoutedEventArgs e)
        {
            if (_editor != null)
            {
                _editor.Focus();
            }
        }

        private string GetDisplayText(object dataItem)
        {
            if (_valueBindingEvaluator == null)
            {
                _valueBindingEvaluator = new BindingEvaluator<string>(new Binding(DisplayMember));
            }
            if (dataItem == null)
            {
                return string.Empty;
            }
            if (string.IsNullOrEmpty(DisplayMember))
            {
                return dataItem.ToString();
            }
            return _valueBindingEvaluator.GetDynamicValue(dataItem);
        }

        private void OnEditorKeyDown(object sender, KeyEventArgs e)
        {
            if (_selectionAdapter != null)
            {
                if (IsDropDownOpen)
                    _selectionAdapter.HandleKeyDown(e);
                else
                    IsDropDownOpen = e.Key == Key.Down || e.Key == Key.Up;
            }
        }

        private void OnEditorLostFocus(object sender, RoutedEventArgs e)
        {
            if (!IsKeyboardFocusWithin)
            {
                IsDropDownOpen = false;
            }
        }

        private void OnEditorTextChanged(object sender, TextChangedEventArgs e)
        {
            if (_isUpdatingText)
                return;
            if (_fetchTimer == null)
            {
                _fetchTimer = new DispatcherTimer();
                _fetchTimer.Interval = TimeSpan.FromMilliseconds(Delay);
                _fetchTimer.Tick += OnFetchTimerTick;
            }
            _fetchTimer.IsEnabled = false;
            _fetchTimer.Stop();
            SetSelectedItem(null);
            if (_editor.Text.Length > 0)
            {
                IsLoading = true;
                IsDropDownOpen = true;
                _itemsSelector.ItemsSource = null;
                _fetchTimer.IsEnabled = true;
                _fetchTimer.Start();
            }
            else
            {
                IsDropDownOpen = false;
            }
        }

        private void OnFetchTimerTick(object sender, EventArgs e)
        {
            _fetchTimer.IsEnabled = false;
            _fetchTimer.Stop();
            if (Provider != null && _itemsSelector != null)
            {
                Filter = _editor.Text;
                if (_suggestionsAdapter == null)
                {
                    _suggestionsAdapter = new SuggestionsAdapter(this);
                }
                _suggestionsAdapter.GetSuggestions(Filter);
            }
        }

        private void OnPopupClosed(object sender, EventArgs e)
        {
            if (!_selectionCancelled)
            {
                OnSelectionAdapterCommit();
            }
        }

        private void OnPopupOpened(object sender, EventArgs e)
        {
            _selectionCancelled = false;
            _itemsSelector.SelectedItem = SelectedItem;
        }

        private void OnSelectionAdapterCancel()
        {
            _isUpdatingText = true;
            _editor.Text = SelectedItem == null ? Filter : GetDisplayText(SelectedItem);
            _editor.SelectionStart = _editor.Text.Length;
            _editor.SelectionLength = 0;
            _isUpdatingText = false;
            IsDropDownOpen = false;
            _selectionCancelled = true;
        }

        private void OnSelectionAdapterCommit()
        {
            if (_itemsSelector.SelectedItem != null)
            {
                _isUpdatingText = true;
                _editor.Text = GetDisplayText(_itemsSelector.SelectedItem);
                SetSelectedItem(_itemsSelector.SelectedItem);
                _editor.SelectionStart = _editor.Text.Length;
                _editor.SelectionLength = 0;
                _isUpdatingText = false;
                IsDropDownOpen = false;
            }
        }

        private void OnSelectionAdapterSelectionChanged()
        {
            _isUpdatingText = true;
            if (_itemsSelector.SelectedItem == null)
            {
                _editor.Text = Filter;
            }
            else
            {
                _editor.Text = GetDisplayText(_itemsSelector.SelectedItem);
            }
            _editor.SelectionStart = _editor.Text.Length;
            _editor.SelectionLength = 0;
            ScrollToSelectedItem();
            _isUpdatingText = false;
        }

        private void SetSelectedItem(object item)
        {
            _isUpdatingText = true;
            SelectedItem = item;
            _isUpdatingText = false;
        }

        #endregion "Methods"

        #region "Nested Types"

        private class SuggestionsAdapter
        {
            #region "Fields"

            private AutoCompleteTextBox _actb;

            private string _filter;

            #endregion "Fields"

            #region "Constructors"

            public SuggestionsAdapter(AutoCompleteTextBox actb)
            {
                _actb = actb;
            }

            #endregion "Constructors"

            #region "Methods"

            public void GetSuggestions(string searchText)
            {
                _filter = searchText;
                _actb.IsLoading = true;
                ParameterizedThreadStart thInfo = new ParameterizedThreadStart(GetSuggestionsAsync);
                Thread th = new Thread(thInfo);
                th.Start(new object[] {
				searchText,
				_actb.Provider
			});
            }

            private void DisplaySuggestions(IEnumerable suggestions, string filter)
            {
                if (_filter != filter)
                {
                    return;
                }
                if (_actb.IsDropDownOpen)
                {
                    _actb.IsLoading = false;
                    _actb._itemsSelector.ItemsSource = suggestions;
                    _actb.IsDropDownOpen = _actb._itemsSelector.HasItems;
                }
            }

            private void GetSuggestionsAsync(object param)
            {
                object[] args = param as object[];
                string searchText = Convert.ToString(args[0]);
                ISuggestionProvider provider = args[1] as ISuggestionProvider;
                IEnumerable list = provider.GetSuggestions(searchText);
                _actb.Dispatcher.BeginInvoke(new Action<IEnumerable, string>(DisplaySuggestions), DispatcherPriority.Background, new object[] {
				list,
				searchText
			});
            }

            #endregion "Methods"
        }

        #endregion "Nested Types"
    }
}